package async;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager;
import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
import org.apache.http.nio.reactor.ConnectingIOReactor;
import org.apache.http.util.EntityUtils;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.protocol.java.sampler.JavaSamplerClient;
import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext;
import org.apache.jmeter.samplers.SampleResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URL;
import java.util.Random;

public class MyAsyncSampler implements JavaSamplerClient {

	private final Logger log = LoggerFactory.getLogger(MyAsyncSampler.class);

	public int MaxTotal;
	public int DefaultMaxPerRoute;
	public int SocketTimeout;
	public int ConnectTimeout;
	public int ConnectionRequestTimeout;

	public String MaxTotal_TEMPLATE_VALUE = "150";
	public String DefaultMaxPerRoute_TEMPLATE_VALUE = "150";
	public String SocketTimeout_TEMPLATE_VALUE = "250";
	public String ConnectTimeout_TEMPLATE_VALUE = "250";
	public String ConnectionRequestTimeout_TEMPLATE_VALUE = "1000";

	public ConnectingIOReactor ioReactor;
	public PoolingNHttpClientConnectionManager connectionManager;
	public CloseableHttpAsyncClient httpclient;
	public RequestConfig requestConfig;


	public JSONObject requests = new JSONObject();
	public String REQUESTS_TEMPLATE_VALUE = "{}";

	public HttpUriRequest uriRequest;
	private MyTransactionController myAsyncSamplerNew = MyTransactionController.myTransactionControllerMap.get(Thread.currentThread().getName());

	@Override
	public Arguments getDefaultParameters() {

		Arguments params = new Arguments();

		params.addArgument("SetMaxTotal", MaxTotal_TEMPLATE_VALUE, "metadata", "");
		params.addArgument("SetDefaultMaxPerRoute", DefaultMaxPerRoute_TEMPLATE_VALUE, "metadata", "");
		params.addArgument("SetSocketTimeout", SocketTimeout_TEMPLATE_VALUE, "metadata", "");
		params.addArgument("SetConnectTimeout", ConnectTimeout_TEMPLATE_VALUE, "metadata", "");
		params.addArgument("SetConnectionRequestTimeout", ConnectionRequestTimeout_TEMPLATE_VALUE, "metadata", "");

		params.addArgument("SetRequests", REQUESTS_TEMPLATE_VALUE, "metadata", "");

		return params;
	}

	@Override
	public void setupTest(JavaSamplerContext context) {

		MaxTotal = context.getIntParameter("SetMaxTotal", 150);
		DefaultMaxPerRoute = context.getIntParameter("SetDefaultMaxPerRoute", 150);
		SocketTimeout = context.getIntParameter("SetSocketTimeout", 250);
		ConnectTimeout = context.getIntParameter("SetConnectTimeout", 250);
		ConnectionRequestTimeout = context.getIntParameter("SetConnectionRequestTimeout", 1000);

		log.info("AsyncSampler [MaxTotal=" + MaxTotal + ", DefaultMaxPerRoute=" + DefaultMaxPerRoute
				+ ", SocketTimeout=" + SocketTimeout + ", ConnectTimeout=" + ConnectTimeout
				+ ", ConnectionRequestTimeout=" + ConnectionRequestTimeout + "]");

		try {

			ioReactor = new DefaultConnectingIOReactor();
			connectionManager = new PoolingNHttpClientConnectionManager(ioReactor);
			connectionManager.setMaxTotal(MaxTotal);
			connectionManager.setDefaultMaxPerRoute(DefaultMaxPerRoute);

			httpclient = HttpAsyncClients.custom().setConnectionManager(connectionManager).build();
			httpclient.start();

			requestConfig = RequestConfig.custom().setSocketTimeout(SocketTimeout).setConnectTimeout(ConnectTimeout)
					.setConnectionRequestTimeout(ConnectionRequestTimeout).build();

		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}

		requests = JSON.parseObject(context.getParameter("SetRequests", "{}"));

		String method = requests.getString("method").toLowerCase();

		switch (method) {
			case "get":

				HttpGet tempGet = new HttpGet(requests.getString("url"));
				tempGet.setConfig(requestConfig);

				uriRequest = tempGet;

				break;

			case "post":

				break;

			default:
				break;
		}
	}

	@Override
	public SampleResult runTest(JavaSamplerContext context) {

		SampleResult sampleResult = new SampleResult();

		sampleResult.setSamplerData(requests.toJSONString());

		sampleResult.sampleStart();

		httpclient.execute(uriRequest, new FutureCallback<HttpResponse>() {

			@Override
			public void cancelled() {

				randomWait();

				sampleResult.sampleEnd();

				sampleResult.setSuccessful(false);
				sampleResult.setResponseData("HTTP CANCELED", "utf-8");

				myAsyncSamplerNew.countDownLatch.countDown();
			}

			@Override
			public void completed(HttpResponse response) {

				randomWait();

				try {
					sampleResult.sampleEnd();

					sampleResult.setResponseCode("200");
					sampleResult.setResponseMessage("OK");
					sampleResult.setDataType("text");
					sampleResult.setURL(new URL(requests.getString("url")));

					sampleResult.setSuccessful(true);
					sampleResult.setResponseData((EntityUtils.toString(response.getEntity(), "UTF-8")), "utf-8");
				} catch (Exception e) {
					log.error(e.getMessage(), e);
				} finally {
					myAsyncSamplerNew.countDownLatch.countDown();
				}
			}

			@Override
			public void failed(Exception exception) {

				randomWait();

				sampleResult.sampleEnd();
				sampleResult.setSuccessful(false);
				sampleResult.setResponseData("HTTP EXCEPTION: " + exception.getMessage(), "utf-8");
				myAsyncSamplerNew.countDownLatch.countDown();
			}

		});

		return sampleResult;
	}

	@Override
	public void teardownTest(JavaSamplerContext context) {

	}

	public void randomWait() {

		try {
			Thread.sleep(new Random().nextInt(1000));
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		}
	}
}
